package com.foo.common.base.utils;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.joda.time.DateTime;

import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;

/**
 * industry-platform项目打包专用
 * 
 * @author Steve
 * 
 */
public enum IndustryPlatformPatchHelper {

	INSTANCE;

	public static String artifactId;

	private static String commitAcc;;
	private static String endAcc;;// default value:HEAD

	public static String projectDirectory;

	private static String workingDirectory;

	private boolean isPomChanged = false;// for jar dependency update hint.
	private boolean isSqlNeedExecuted = false;
	private boolean isLogbackXmlChanged = false;
	private boolean isHibernateMysqlChanged = false;
	private boolean isHibernateMysqlPoolChanged = false;

	private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory
			.getLogger(IndustryPlatformPatchHelper.class);
	private static final String pathName = ("patch-"
			+ FooUtils.toDateFromYear2Second(new DateTime()) + "\\")
			.replaceAll(":", ".");

	public static void main(String[] args) throws IOException {

		Properties p = FooUtils
				.getPropertiesFromResourceFile(("industry-platform-patch.properties"));
		artifactId = p.getProperty("artifactId");
		commitAcc = p.getProperty("commitAcc");
		endAcc = p.getProperty("endAcc");
		projectDirectory = p.getProperty("projectDirectory");
		workingDirectory = p.getProperty("workingDirectory");

		File tmpDir = new File(workingDirectory);
		if (!tmpDir.isDirectory()) {
			FileUtils.forceMkdir(tmpDir);
			logger.warn("create dir success now:{}", tmpDir.getPath());
			// Files.createParentDirs(tmpDir);
		}

		try {
			IndustryPlatformPatchHelper.INSTANCE.patchFromGitDiff(
					workingDirectory, pathName, commitAcc, endAcc);
		} catch (Exception e) {
			String directory = workingDirectory + pathName;
			logger.error("{}", e);
			logger.error("delete the folder on exception now:{}", directory);
			FileUtils.deleteQuietly(new File(directory));
		}
	}

	public void patchFromGitDiff(String workingDirectory, String pathName,
			String commitAcc, String endAcc) throws Exception {

		Set<String> myErrorProjectNameSet = Sets.newHashSet();// Error set

		logger.info("patch name will be {}", pathName);

		FileUtils.deleteDirectory(new File(workingDirectory + pathName));

		Set<String> saveOrUpdateClassSet = Sets.newHashSet();
		Set<String> configFileSet = Sets.newHashSet();
		Set<String> webappSourceSet = Sets.newHashSet();
		Set<String> excludeSet = Sets.newHashSet();

		Map<String, Set<String>> bean = GitHelper
				.getGitUpdateLinesFromCmd_Diff(projectDirectory, artifactId,
						commitAcc, endAcc);

		Set<String> updatedFileNameSet = bean.get("updatedFileNameSet");
		Set<String> deletedFileNameSetTmp = bean.get("deletedFileNameSet");
		Set<String> deletedFileNameSetResult = Sets.newHashSet();

		for (String tmp : deletedFileNameSetTmp) {
			tmp = FilenameUtils.normalize(tmp);
			if (!tmp.startsWith(artifactId)) {
				myErrorProjectNameSet.add(tmp);
				logger.error("error add:{}", tmp);
				continue;
			}
			deletedFileNameSetResult.add(tmp);
		}

		for (String tmp : updatedFileNameSet) {

			tmp = FilenameUtils.normalize(tmp);

			logger.info("tmp is:{}", tmp);

			if (!tmp.startsWith(artifactId)) {
				myErrorProjectNameSet.add(tmp);
				logger.error("error add:{}", tmp);
				continue;
			}

			if (tmp.contains("pom.xml")) {
				isPomChanged = true;
				logger.warn("maven pom file detected,skip this file.But you should add jar dependency");
				excludeSet.add(tmp);
				continue;
			}

			if (tmp.contains("deployment.sql")) {
				isSqlNeedExecuted = true;
				logger.warn("sql executed on server detected,skip this file.But you should execute sql on server.");
				excludeSet.add(tmp);
				continue;
			}

			if (tmp.contains("hibernate-localhost-mysql.properties")) {
				isHibernateMysqlChanged = true;
				logger.warn("hibernate-localhost-mysql.properties changed!");
				excludeSet.add(tmp);
				continue;
			}
			if (tmp.contains("logback.xml")) {
				isLogbackXmlChanged = true;
				logger.warn("logback.xml changed!");
				excludeSet.add(tmp);
				continue;
			}
			if (tmp.contains("pool-mysql.properties")) {
				isHibernateMysqlPoolChanged = true;
				logger.warn("pool-mysql.properties changed!");
				excludeSet.add(tmp);
				continue;
			}
			if (tmp.contains(".gitignore")) {
				logger.warn(".gitignore detected!");
				excludeSet.add(tmp);
				continue;
			}

			if (tmp.startsWith(artifactId + "\\src\\main\\java")) {
				saveOrUpdateClassSet.add(tmp.replaceAll("/", "\\"));
				continue;
			} else if (tmp.startsWith(artifactId + "\\src\\main\\resources")) {
				configFileSet.add(tmp);
				continue;
			} else {
				webappSourceSet.add(tmp);
			}

		}

		FooClassCompileHelper classHelper = FooClassCompileHelper
				.custom()
				.setWorkingDirectory("D:\\tmp\\" + artifactId)
				.setSourceDirectory(
						projectDirectory + artifactId + "\\target\\classes")
				.setArtifactId(artifactId).build();

		Map<String, Integer> classSizeResultMap = classHelper
				.copyJavaSrcAndClass(saveOrUpdateClassSet);
		int normalClassSize = classSizeResultMap.get("normalClassSize");
		int innerClassSize = classSizeResultMap.get("innerClassSize");

		File srcDir = new File(workingDirectory + "target\\");
		File destDir = new File(workingDirectory + pathName
				+ "WEB-INF\\classes");

		// 只要有target存在就可以判定是存在编译了的class类的
		Preconditions.checkArgument(srcDir.isDirectory(), "srcDir of:" + srcDir
				+ " do not exist or is not a directory");

		FileUtils.copyDirectory(srcDir, destDir);

		logger.info("coping class over,ready to copy webApp resources.");

		// String gitBase = projectDirectory + "\\" + artifactId + "\\";
		String tmpOldPath = "";
		String tmpRelativePath = "";
		for (String filePath : webappSourceSet) {

			logger.trace("filePath here is:{}", filePath);

			File oldFile = new File(projectDirectory + filePath);

			tmpOldPath = FilenameUtils.getFullPath(oldFile.getPath());
			logger.trace("tmpOldPath path:{}", tmpOldPath);
			logger.trace("tmpRelativePath path:{}", projectDirectory
					+ artifactId + "\\src\\main\\webapp\\");

			tmpRelativePath = tmpOldPath.replace(projectDirectory + artifactId
					+ "\\src\\main\\webapp\\", "");

			logger.trace("relativePath is:{}", tmpRelativePath);

			FileUtils.copyFileToDirectory(
					new File(projectDirectory + filePath), new File(
							workingDirectory + pathName + tmpRelativePath));
		}

		logger.info("coping webApp resources over,ready to copy config files.");

		String tmpDestDir = "";
		for (String filePath : configFileSet) {

			File oldFile = new File(projectDirectory + filePath);
			String relativePath = "WEB-INF\\classes\\"
					+ FilenameUtils.getFullPath(oldFile.getPath()).replace(
							projectDirectory + artifactId
									+ "\\src\\main\\resources\\", "");

			tmpDestDir = workingDirectory + pathName + relativePath;

			logger.trace(
					"filePath is:{} and relativePath is:{} and tmpDestDir is:{}",
					filePath, relativePath, tmpDestDir);

			FileUtils
					.copyFileToDirectory(new File(projectDirectory + filePath),
							new File(tmpDestDir));
		}

		/*
		 * check patch success or not,Start
		 */

		Preconditions.checkArgument(
				(updatedFileNameSet.size() == saveOrUpdateClassSet.size()
						+ configFileSet.size() + webappSourceSet.size()
						+ excludeSet.size()),
				"updatedFileNameSet do not match all sets.");

		int allFileUpdatedSize = normalClassSize + innerClassSize
				+ webappSourceSet.size() + configFileSet.size();
		logger.info("all files your update size exclude folder will be:{}",
				allFileUpdatedSize);
		int currentFileNum = FooUtils.getDirectoryFilesNumber(new File(
				workingDirectory + pathName));
		Preconditions.checkArgument(currentFileNum == allFileUpdatedSize,
				"Class number do not match.");
		/*
		 * check patch success or not,End
		 */

		// Write to file start,2015-05-18 verison.
		logger.warn("excludeSet as:{}", excludeSet);

		StringBuilder tmpBuilder = new StringBuilder();

		if (isPomChanged) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("pom文件变化,需要手动添加或删除jar包:(如果不添加，会导致功能异常)"
					+ isPomChanged + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
		}

		if (isSqlNeedExecuted) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder
					.append("sql文件变化，请执行sql文件【deployment.sql】里面的sql:(如果不执行，会导致功能异常或者服务器无法启动)"
							+ isSqlNeedExecuted + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
		}

		if (isHibernateMysqlChanged) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder
					.append("监测到hibernate-localhost-mysql.properties配置文件变化,该文件在一般情况下不应该被修改，修改文件可能会导致服务器数据库连接错误！");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
		}

		if (isLogbackXmlChanged) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder
					.append("监测到logback.xml配置文件变化,该文件在一般情况下不应该被修改，修改文件可能会导致服务器日志异常！");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
		}

		if (isHibernateMysqlPoolChanged) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder
					.append("监测到pool-mysql.properties配置文件变化,该文件在一般情况下不应该被修改，修改文件会导致数据库连接池异常！");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
		}

		if (deletedFileNameSetResult.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("需要手动从服务器上删除的文件如下：(如果不删除，会导致功能异常)" + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : deletedFileNameSetResult) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		if (excludeSet.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("被排除的更新列表如下:" + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : excludeSet) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		if (saveOrUpdateClassSet.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("需要更新的java类列表如下:" + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : saveOrUpdateClassSet) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		if (configFileSet.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("需要更新的配置文件列表如下:" + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : configFileSet) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		if (webappSourceSet.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("需要更新的webapp资源列表如下:" + "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : webappSourceSet) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		if (myErrorProjectNameSet.size() >= 1) {
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			tmpBuilder.append("错误的更新列表如下:(多个GIT在同一目录下会导致该错误，请考虑分git创建工程)"
					+ "\n");
			tmpBuilder.append(FooUtils.getSeparateLine() + "\n");
			for (String tmp : myErrorProjectNameSet) {
				tmpBuilder.append(tmp + "\n");
			}
		}

		String directory = workingDirectory + pathName;
		File updateFile = new File(directory + "update.txt");

		FileUtils.write(updateFile, tmpBuilder.toString());
		logger.info("更新文档生成目录:{}", directory);
		logger.info("更新文档生成日志:{}", updateFile.getAbsolutePath());

	}
}
